# -*- coding: utf-8 -*-
import hall_callback
import t_const, t_error, t_cmd, t_wizard
#import t_struct
#import t_factroy_s
import t_game_player_s
#import t_sprite_config, t_stage_config
import t_room
import random, cPickle

import t_mapevent_s

#游戏逻辑类，单局游戏的全局数据保存在这里
class CMap(object):
	#构造函数
	def __init__(self, objRoom):
		super(CMap, self).__init__()
		self.objRoom = objRoom				#房间对象的引用，消息发送和写日志用到
		self.objMsgMgr = hall_callback.get_game_room_msgmgr()		#传输对象的引用，消息打包的时候用到
		self.register_recv()		#注册游戏子事件的响应函数
		self.reset()						#初始化游戏内全局数据


	#自我清理的函数，释放此对象前调用
	def clean(self):
		self.reset_map()
		self.reset()
		self.objRoom = None
		self.objMsgMgr = None

	#此函数注册游戏子事件的具体响应函数
	def register_recv(self):
		self.dicEventFunc = {}
		self.dicEventFunc[t_cmd.C_TUREND] = self.on_turend
		self.dicEventFunc[t_cmd.C_MOVE] = self.on_move
		self.dicEventFunc[t_cmd.C_RUN] = self.on_run
		self.dicEventFunc[t_cmd.C_ATK] = self.on_atk
		self.dicEventFunc[t_cmd.C_PICKITEM] = self.on_pickitem

	#此函数对游戏内全局数据进行初始化或重置
	def reset(self):
		self.iStatus = t_const.GAME_WAIT		#游戏状态，初始化为准备（非进行游戏）
		self.gametime = -1	# 游戏计时器
		self.iStage = None		#当前关卡，初始化为None
		self.iCurFrame = 0		#当前帧数，初始化为0
		self.objTimeLine = None		#管理怪物和阳光刷新数据的对象，初始化为None
		self.dicTrackSet = {}			#当前关卡的地形数据的字典，key是轨道编号，value是一个代表地形的int，可能为草地、水域或屋顶
		self.dicPlayers = {}			#管理当前游戏内玩家对象的字典，key是玩家的side，value是游戏内玩家对象
		self.dicPlayersuid = {}
		#self.iCurFreeID = t_const.ID_START_SERVER		#当前已经使用到的最大的精灵id
		#self.dicUsedID = {}				#管理当前存在的全部精灵id的字典，key是精灵的id，value是1
		#self.dicTowerBase = {}		#管理当前存在的全部基座（可种植植物的地点）的字典， key是基座的唯一id，value是基座对象
		#self.dicTower = {}				#管理当前存在的全部植物的字典，key是植物的唯一id，value是植物对象
		#self.dicMonster = {}			#管理当前存在的全部僵尸的字典，key是僵尸的唯一id，value是僵尸对象
		#self.dicMissile = {}			#管理当前存在的全部子弹的字典，key是子弹的唯一id，value是子弹对象
		#self.dicItem = {}					#管理当前存在的全部奖励物品（阳光，银币等）的字典，key是物品的唯一id，value是物品对象
		self.iWinScore = 0				#本局的胜利得分
		self.iLoseScore = 0				#本局的失败得分"""

	#此函数释放和清理游戏内的全局数据，一般在游戏结束的时候调用
	def reset_map(self):
		self.gametime = None
		#清空地形数据字典
		#self.dicTrackSet.clear()
		#清空玩家字典并释放其中的玩家对象
		"""for player in self.dicPlayers.itervalues():
			player.clean()
		self.dicPlayers.clear()
		#清空现存精灵id字典
		self.dicUsedID.clear()
		#清空基座字典并释放基座对象
		for basis in self.dicTowerBase.itervalues():
			basis.clean()
		self.dicTowerBase.clear()
		#清空植物字典并释放植物对象
		for tower in self.dicTower.itervalues():
			tower.clean()
		self.dicTower.clear()
		#清空僵尸字典并释放僵尸对象
		for monster in self.dicMonster.itervalues():
			monster.clean()
		self.dicMonster.clear()
		#清空子弹字典并释放子弹对象
		for missile in self.dicMissile.itervalues():
			missile.clean()
		self.dicMissile.clear()
		#清空物品字典并释放物品对象
		for item in self.dicItem.itervalues():
			item.clean()
		self.dicItem.clear()"""

	#此函数生成一个可用的精灵id，并维护精灵id字典，大致算法是对当前已经用到的精灵id进行递增，并加入已用精灵字典，然后返回id
	def generate_id(self, iType):
		pass
		"""for i in xrange(t_const.ID_MAX_GENERATE):
			self.iCurFreeID += 1
			#精灵id具有区间限制，如果达到了区间的最大值，那么就从最小值开始重新递增，不过设计上单局游戏不应该达到区间最大值，所以同时还要加上特殊的日志，用于调整区间设置的依据
			if self.iCurFreeID >= t_const.ID_END_SERVER:
				self.iCurFreeID = t_const.ID_START_SERVER
				self.objRoom.cglog(t_const.LOGTYPE_DESIGN, "[SPRITE_ID_EXCEED]:[frame=%d;stage=%d]" % (self.iCurFrame, self.iStage))
			if self.dicUsedID.has_key(self.iCurFreeID):
				continue
			self.dicUsedID[self.iCurFreeID] = 1
			return self.iCurFreeID
		#如果超过最大循环次数依旧无法找到可用的精灵id，说明数值区间设计存在重大问题，必须立刻调整
		self.objRoom.cglog(t_const.LOGTYPE_DESIGN, "[NO_AVAILABLE_ID]:[frame=%d;stage=%d]" % (self.iCurFrame, self.iStage))
		return None"""


	#此函数处理游戏开始的相关逻辑
	def on_game_start(self, stage, lstPlayer):
		#重置游戏内全局数据
		self.reset()
		#设置游戏状态为游戏中
		self.iStatus = t_const.GAME_PLAYING
		#保存当前关卡key
		self.iStage = stage


		#读取关卡配置
		#config = td_stage_config.DATA.get(stage)

		#初始化游戏内玩家对象
		for room_player in lstPlayer:
			objPlayer = t_game_player_s.CGameplayer(room_player)
			objPlayer.init_game(0, "")
			self.dicPlayers[objPlayer.iside] = objPlayer
			self.dicPlayersuid[objPlayer.uid] = objPlayer

		self.game_event = t_mapevent_s.CMapEvent(self)
		# 加载地图事件
		#self.game_event.loadmapevent("open", self.dicPlayersuid)

		#加载地图信息
		#scene = config["scene"]
		self.iWinScore = 10#config["win_score"]
		self.iLoseScore = 0#config["lose_score"]
		#加载地形信息，生成基座字典
		#for y in xrange(len(scene)):
		#	landform = scene[y]
		#	if not self.dicTrackSet.has_key(landform):
		#		self.dicTrackSet[landform] = []
		#	self.dicTrackSet[landform].append(y)
		#	for x in xrange(td_const.MAP_GRID_WIDTH):
			#	iID = x + y * td_const.MAP_GRID_WIDTH
			#	self.dicTowerBase[iID] = td_struct.CTowerBase(iID, landform, x, y, None)

		#生成怪物和阳光刷新数据对象
		#self.objTimeLine = td_struct.CTimeLine(config)
		#发送游戏开始消息
		self.send_game_start(stage)

	#此函数用于检测是否达到游戏胜利条件
	def check_game_end(self, second):
		#如果还有固定怪物未刷新，则不算胜利
		"""if not self.objTimeLine.is_empty_force(second):
			return False
		#固定怪物都刷新了，那么消灭所有现存怪物后游戏胜利
		if len(self.dicMonster) == 0:
			return True
		else:
			return False"""
		pass

	#此函数检测游戏中有效（未退出或失败）的玩家是否为0，假如有效玩家为0则游戏强制结束
	def check_empty_game(self):
		"""total = len(self.dicPlayers)
		leave = 0
		for player in self.dicPlayers.itervalues():
			if player.bIsLeave:
				leave += 1
		if total - leave <= 1:
			return True
		else:
			return False"""
		pass

	#此函数用于处理游戏结束的相关逻辑
	def game_end(self, win):
		#根据玩家当局的具体表现对游戏进行结算，并生成游戏内玩家对象的临时列表
		lstPlayer = []
		for player in self.dicPlayers.itervalues():
			player.on_game_end(win, self.iWinScore,	self.iLoseScore)
			lstPlayer.append(player)

		#游戏状态切换到准备
		self.iStatus = t_const.GAME_WAIT
		#调用房间逻辑的on_game_end函数，处理需要保留的历史数据
		"""self.objRoom.on_game_end(win, lstPlayer)
		#重置游戏内全局数据
		self.reset_map()"""
		return lstPlayer

	#此函数每个逻辑帧调用一次，处理时间驱动的逻辑
	def logic_frame(self):
		#当前帧数递增
		self.iCurFrame += 1
		#每秒一次发送游戏时间
		if (self.iCurFrame % t_const.FPS_SERVER) == 0:
			#second = self.iCurFrame / t_const.FPS_SERVER
			#print self.iCurFrame
			self.send_gametime()
		# 事件
		self.game_event.update()
		pass

	#此函数每秒调用一次，根据关卡配置刷新僵尸
	def generate_monster(self, second):
		#self.send_gametime()
		#t_mapevent_s.inst.update()
		pass

	#此函数每秒调用一次，根据关卡配置生成阳光
	def generate_sun(self, second):
		#获取当前秒需要生成的阳光种类列表
		"""lstSun = self.objTimeLine.get_sun(second)
		if not lstSun:
			return
		iMaster = random.choice(self.dicPlayers.keys())
		for iJob in lstSun:
			iID = self.generate_id(t_const.OBJECT_ITEM)
			if iID is None:
				continue
			sprite = self.new_sprite(iID, t_const.OBJECT_ITEM, iJob, iMaster)
			if not sprite:
				#无法生成阳光一般说明此阳光种类不存在
				self.objRoom.cglog(t_const.LOGTYPE_CONFIG, "[FAIL_TO_CREATE_SPRITE][stage=%d;second=%d;iJob=%d]" % (self.iStage, second, iJob))
				continue
			sprite.set_mapmgr(self)
			#向客户端广播有新阳光生成的消息
			self.send_new_item(sprite.iID, sprite.iType, sprite.iJob, sprite.iMaster, -1, -1)"""
		pass

	#此函数在游戏中玩家离开房间时由房间逻辑调用，记录此玩家不再是“有效玩家”，并判断是否因为全部为无效玩家而结束游戏
	def player_leave(self, iSide):
		"""objPlayer = self.dicPlayers.get(iSide, None)
		if objPlayer:
			objPlayer.bIsLeave = True
		if self.check_empty_game():
			self.game_end(t_const.FACTION_NONE)"""
		pass

	#此函数在客户端发送游戏子事件时由房间逻辑调用，对子事件进行数据解包并根据子事件类型调用具体的子事件处理函数
	def on_game_event(self, iSide, frame, event, data):
		#print "server:on_game_event:",iSide,frame,event,data
		if self.dicEventFunc.has_key(event):
			param = [iSide, ]
			param.extend(cPickle.loads(data))
			apply(self.dicEventFunc[event], param)
		pass


#-------------------------------------------------向客户端广播消息
	def on_turend(self, obj, uid, rotate):
		#print "send:on_turend",obj,names,rotate
		param = (uid,rotate)
		self.send_game_event(t_cmd.S_TUREND, param)

	def on_move(self,obj, uid, ismove, dirx, diry, speed):
		param = (uid, ismove, dirx, diry, speed)
		self.send_game_event(t_cmd.S_MOVE, param)

	def on_run(self, obj, uid, posx, posy):
		param = (uid, posx, posy)
		self.dicPlayersuid[uid].pos = (posx,posy)
		#print "update%s pos"%uid
		self.send_game_event(t_cmd.S_RUN, param)

	def on_atk(self, obj, uid, pos, dir, weapon):
		param = (uid, pos, dir, weapon)
		self.send_game_event(t_cmd.S_ATK, param)

	def on_pickitem(self, obj, uid, names):
		param = (uid, names)
		# 判断拾取
		if self.dicPlayersuid[uid].pickItem(names):
			self.send_game_event(t_cmd.S_PICKITEM, param)

	# 游戏时间发送
	def send_gametime(self):
		self.gametime += 1
		param = (self.gametime, 0)
		#print "send_time:",self.gametime
		#self.send_game_event(t_cmd.S_GAMETIME, param)


#----------------------------------------------------------------
	#打包并发送游戏开始的消息，stage：当前关卡的id；player_list：此局游戏的玩家列表；每个玩家的信息包括：side：玩家标识；energy：起始阳光/能量数量；faxtion：阵营（植物方还是僵尸方）；card_list：本局选择的可种植/创建的精灵种类列表
	def send_game_start(self, iStage):
		_list = []
		for player in self.dicPlayers.itervalues():
			tmp_list = []
			#for iJob in player.lstCanBuild:
			#	int_obj = self.objMsgMgr.game_single_int(value=iJob)
			#	tmp_list.append(int_obj)
			obj = self.objMsgMgr.t_game_player_start_info(side=player.iside,ifac=1, money=0, dicsprite=0)
			_list.append(obj)
		obj = self.objMsgMgr.game_s_game_start(stage=iStage, player_list=_list)
		self.objRoom.send_all(obj)

	#打包并发送游戏子事件的消息，frame：子事件发生的服务端时间帧；event_id：子事件类型；param：子事件具体参数（使用cPickle打包成一个字符串）
	def send_game_event(self, event, param_list):
		_str = cPickle.dumps(param_list)
		obj = self.objMsgMgr.game_s_game_event(frame=self.iCurFrame, event_id=event, param=_str)
		self.objRoom.send_all(obj)

	# 除某人外的其它人
	def send_game_event_other(self, event, param_list, uid):
		_str = cPickle.dumps(param_list)
		obj = self.objMsgMgr.game_s_game_event(frame=self.iCurFrame, event_id=event, param=_str)
		self.objRoom.send_all(obj, self.dicPlayersuid[uid].hid)

	def send_game_event_p(self, event, param_list, uid):
		_str = cPickle.dumps(param_list)
		obj = self.objMsgMgr.game_s_game_event(frame=self.iCurFrame, event_id=event, param=_str)
		self.objRomm.send_to_user(self.dicPlayersuid[uid].hid, obj)
		pass